              package com.java.diagnostics.visualizer.impl.extensions;
              
              import com.java.diagnostics.visualizer.impl.factory.LogFactory;
              import com.java.diagnostics.visualizer.impl.util.Messages;
              import com.java.diagnostics.visualizer.recommender.Recommendation;
              import java.text.MessageFormat;
              import java.util.ArrayList;
              import java.util.HashMap;
              import java.util.HashSet;
              import java.util.Iterator;
              import java.util.LinkedList;
              import java.util.List;
              import java.util.ListIterator;
              import java.util.logging.Level;
              import java.util.logging.Logger;
              import org.eclipse.core.runtime.CoreException;
              import org.eclipse.core.runtime.IConfigurationElement;
              import org.eclipse.core.runtime.IExtension;
              import org.eclipse.core.runtime.IExtensionPoint;
              import org.eclipse.core.runtime.IExtensionRegistry;
              import org.eclipse.core.runtime.Platform;
              
              public class RecommendationsRegistry extends GCAndMemoryVisualizerExtensionRegistry
              {
                private String CLASS = "class";
                private String REQUIRES = "requires";
                private String ADDS = "adds";
                private String TYPE = "type";
                private String TUPLE_DATA = "TupleData";
                private String LASTID = "LASTID";
                private String STRUCTURED_DATA = "StructuredData";
                private String GC_ATTRIBUTES = "com.java.diagnostics.visualizer.parser.vgc.constants.VGCLabels.GC_ATTRIBUTES";
                private LinkedList<Recommendation> classes;
                private int added;
                private HashMap<String, ArrayList<String>> dataWeHave;
                private HashSet<IExtension> runLast;
                private static RecommendationsRegistry registry = null;
              
                private static final Logger TRACE = LogFactory.getTrace(RecommendationsRegistry.class);
              
                protected String getExtensionPointID()
                {
                  return "com.java.diagnostics.visualizer.recommender.recommender";
                }
              
                public static RecommendationsRegistry getInstance()
                {
                  if (registry == null) {
                    registry = new RecommendationsRegistry();
                  }
                  return registry;
                }
              
                public Recommendation[] instantiateRecommendations() {
                  List recommendations = instantiateExtensions();
              
                  if (recommendations == null) {
                    return null;
                  }
                  return 
                    (Recommendation[])recommendations
                    .toArray(new Recommendation[0]);
                }
              
                protected List<Recommendation> instantiateExtensions()
                {
                  IExtension[] extensions = (IExtension[])null;
                  HashSet extensionsToAdd = null;
                  this.runLast = new HashSet();
              
                  this.dataWeHave = new HashMap();
              
                  IExtensionRegistry registry = Platform.getExtensionRegistry();
              
                  if (registry != null) {
                    IExtensionPoint extensionPoint = registry
                      .getExtensionPoint(getExtensionPointID());
              
                    if (extensionPoint != null)
                    {
                      extensions = extensionPoint.getExtensions();
                      extensionsToAdd = new HashSet(extensions.length);
                      for (int i = 0; i < extensions.length; i++) {
                        String identifier = extensions[i].getSimpleIdentifier();
                        if ((identifier != null) && 
                          (identifier.equalsIgnoreCase(this.LASTID))) {
                          this.runLast.add(extensions[i]);
                        }
                      }
              
                      this.classes = new LinkedList();
                    }
                  }
              
                  this.added = 0;
                  if (extensions != null)
                  {
                    for (int i = 0; i < extensions.length; i++)
                    {
                      if (requiresOnlyTupleData(extensions[i])) {
                        addToList(extensions[i]);
                        addDataToList(extensions[i]);
                      } else {
                        extensionsToAdd.add(extensions[i]);
                      }
                    }
              
                    if (!this.runLast.isEmpty()) {
                      extensionsToAdd.removeAll(this.runLast);
                    }
              
                    HashSet extensionsToAddCopy = null;
                    while (this.classes.size() < extensions.length - this.runLast.size()) {
                      extensionsToAddCopy = (HashSet)extensionsToAdd
                        .clone();
                      Iterator i = extensionsToAddCopy.iterator();
                      int added = 0;
                      while (i.hasNext()) {
                        IExtension current = (IExtension)i.next();
                        if (haveRequiredData(current)) {
                          addToList(current);
                          addDataToList(current);
                          extensionsToAdd.remove(current);
                          added++;
                        }
                      }
                      if (added == 0) {
                        reportMissingRecommendations(extensionsToAdd);
                        break;
                      }
                    }
              
                    Iterator j = this.runLast.iterator();
                    while (j.hasNext()) {
                      IExtension current = (IExtension)j.next();
                      addToList(current);
                    }
                  }
                  return this.classes;
                }
              
                private void reportMissingRecommendations(HashSet<IExtension> extensionsToAdd)
                {
                  LinkedList notAdded = new LinkedList();
                  Iterator j = extensionsToAdd.iterator();
                  IConfigurationElement[] currentElements;
                  while (j.hasNext()) {
                    IExtension current = (IExtension)j.next();
                    currentElements = current
                      .getConfigurationElements();
                    Recommendation rec = null;
                    try {
                      rec = (Recommendation)currentElements[0]
                        .createExecutableExtension(this.CLASS);
                    } catch (CoreException ce) {
                      ce.printStackTrace();
                    }
              
                    if (!this.classes.contains(rec))
                    {
                      IConfigurationElement[] elements = current
                        .getConfigurationElements();
                      for (int k = 0; k < elements.length; k++)
                      {
                        try
                        {
                          Object executableExtension = elements[k]
                            .createExecutableExtension(this.CLASS);
                          notAdded.add((Recommendation)executableExtension);
                          this.added += 1;
                        } catch (CoreException ce) {
                          ce.printStackTrace();
                        }
                      }
                    }
                  }
                  for (Object rec : notAdded)
                    TRACE.log(Level.WARNING, MessageFormat.format(
                      Messages.getString("RecommendationRegistry.found.problem"), 
                      new Object[] { rec }));
                }
              
                private boolean requiresOnlyTupleData(IExtension extension)
                {
                  boolean answer = true;
                  IConfigurationElement[] elements = extension.getConfigurationElements();
                  for (int j = 0; j < elements.length; j++) {
                    IConfigurationElement[] children = elements[j]
                      .getChildren(this.REQUIRES);
              
                    for (int i = 0; i < children.length; i++) {
                      String type = children[i].getAttribute(this.TYPE);
                      if ((!type.equalsIgnoreCase(this.TUPLE_DATA)) && 
                        (!isGCAttribute(children[i]))) {
                        answer = false;
                      }
                    }
                  }
                  return answer;
                }
              
                private boolean isGCAttribute(IConfigurationElement element) {
                  String type = element.getAttribute(this.TYPE);
                  String name = element.getAttribute("name");
              
                  return (this.STRUCTURED_DATA.equalsIgnoreCase(type)) && 
                    (this.GC_ATTRIBUTES.equalsIgnoreCase(name));
                }
              
                private void addToList(IExtension extension) {
                  IConfigurationElement[] elements = extension.getConfigurationElements();
                  for (int j = 0; j < elements.length; j++)
                    try {
                      Object executableExtension = elements[j]
                        .createExecutableExtension(this.CLASS);
                      this.classes.add((Recommendation)executableExtension);
                      this.added += 1;
                    } catch (CoreException ce) {
                      ce.printStackTrace();
                    }
                }
              
                private void addDataToList(IExtension extension)
                {
                  IConfigurationElement[] elements = extension.getConfigurationElements();
              
                  for (int j = 0; j < elements.length; j++)
                  {
                    IConfigurationElement[] children = elements[j].getChildren(this.ADDS);
                    for (int i = 0; i < children.length; i++) {
                      String type = children[i].getAttribute(this.TYPE);
                      String name = children[i].getAttribute("name");
                      ArrayList names = null;
                      if (this.dataWeHave.containsKey(type))
                        names = (ArrayList)this.dataWeHave.get(type);
                      else {
                        names = new ArrayList();
                      }
                      names.add(name);
                      this.dataWeHave.put(type, names);
                    }
                  }
                }
              
                private boolean haveRequiredData(IExtension extension)
                {
                  boolean answer = false;
                  IConfigurationElement[] elements = extension.getConfigurationElements();
                  for (int j = 0; j < elements.length; j++) {
                    IConfigurationElement[] children = elements[j]
                      .getChildren(this.REQUIRES);
              
                    for (int i = 0; i < children.length; i++) {
                      String type = children[i].getAttribute(this.TYPE);
                      String name = children[i].getAttribute("name");
                      if ((!type.equalsIgnoreCase(this.TUPLE_DATA)) && 
                        (this.dataWeHave.containsKey(type))) {
                        ArrayList names = (ArrayList)this.dataWeHave.get(type);
                        ListIterator list = names.listIterator();
                        while (list.hasNext()) {
                          String next = (String)list.next();
                          if (next.equalsIgnoreCase(name)) {
                            answer = true;
                          }
                        }
                      }
                    }
                  }
              
                  return answer;
                }
              }

